package com.gezi.admin.security.filter;

import com.gezi.admin.security.SecurityUtil;
import com.gezi.admin.security.annotation.SecurityIgnoreUrlUtil;
import com.gezi.admin.security.exception.LoginAuthenticationException;
import com.gezi.admin.security.model.LoginClientEnum;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @Author 格子软件
 * @createDate 2024/2/10 09:00
 * @contact 公众号：格子软件 微信：13716343106 邮箱：1424529270@qq.com
 */
public class AccessInterceptorFilter extends AuthorizationFilter {

    //FILTER_APPLIED：针对当前请求，当前 filter 是否执行过一次的标志 key。
    // 若没有执行过会将该常量作为 key 添加到 request Attribute 中，
    // 后续如果又进入当前 filter bean 中（一定执行过一次），判断 request 中有这个 key ，就直接跳过当前业务调取下一个过滤链。
    //思考：为什么加这个呢？
    private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied";

    public AccessInterceptorFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
        super(authorizationManager);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //封装对象，方便调用后续过滤器
        FilterInvocation fi =  new FilterInvocation(request, response, chain);

        //如果当前请求已经调用过这个 filter bean，就直接执行后续过滤器
        if ((fi.getRequest() != null)
                && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
                && isObserveOncePerRequest()) {
            // filter already applied to this request and user wants us to observe
            // once-per-request handling, so don't re-do security checking
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());

            logger.info("request " + fi.getRequestUrl() + " has already been security checking.");
        }
        //如果当前请求没有调用过这个 filter bean，就需要执行业务
        else {
            logger.info("request " + fi.getRequestUrl() + " will be security checking.");

            // 没有执行过就是第一次，就需要修改状态为已执行过
            if (fi.getRequest() != null && isObserveOncePerRequest()) {
                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
            }
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            //OPTIONS请求直接放行
            if (httpRequest.getMethod().equals(HttpMethod.OPTIONS.toString())) {
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }

            // 测试用户
            if(SecurityUtil.weappTestEnable && !LoginClientEnum.admin.isSame(httpRequest.getHeader("client"))){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }

            //swagger放行
            List<String> swaggerIgnoreList = Arrays.asList(
                    "/server/v2/**",
                    "/server/doc.html",
                    "/server/swagger**/**",
                    "/server/webjars/**"
                    );
            PathMatcher pathMatcher = new AntPathMatcher();
            for (String path : swaggerIgnoreList) {
                if (pathMatcher.match(path, httpRequest.getRequestURI())) {
                    fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                    return;
                }
            }

            //白名单请求直接放行。这里采用注解的形式判断
            for (String path : Arrays.asList("/server/file/**")) {
                if (pathMatcher.match(path, httpRequest.getRequestURI())) {
                    fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                    return;
                }
            }
            // 白名单URL【注解】
            if(SecurityIgnoreUrlUtil.isIgnoredUrl(httpRequest)){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
                return;
            }

            //进行token认证
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if(authentication instanceof UsernamePasswordAuthenticationToken){
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            }else{
                throw LoginAuthenticationException.JWT_EXPIRED;
            }
        }
    }
}
